package model;

import java.io.IOException;
import java.net.Socket;
import java.sql.Timestamp;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;


import dao.DaoException;
import dao.DaoFactory;

/**
 * The server model that holds all the data
 * @author MUSAB
 *
 */
public class Server {
	
	// Server port
	private final  static int port = 6789;
	
	// List of online clients
	private List<Client> connectedClients;
	
	// List of emails that have not been sent
	private List<Email> emailsToSend;
	
	//dao factory member
	private DaoFactory daoFactory;
	
	/**
	 * Server constructor
	 */
	public Server(){
		
		initDao();
		
		connectedClients = new ArrayList<Client>();
		emailsToSend = new ArrayList<Email>();

		// Test code
//		createTestEmails();
		
		try {
			loadEmails();
			
			for(Email em: emailsToSend){
				System.out.println(em.toString());
			}
			
			
		} catch (DaoException e) {
			System.out.println("Server: Could not load emails, exiting..");
			System.exit(0);
		}
	
	}
	
	/**
	 * Remove the client from the
	 */
	public void destroyConnectedClient(Client client){

		System.out.println("DESTROYED CLIENT!!!!");
		
		try {
			client.getSocket().getOutputStream().close();
			client.getSocket().close();
		} catch (IOException e) {}
		System.out.println("Destroyed connected client: " + client.getName());
		connectedClients.remove(client);
	}
	
	// test code
	private void createTestEmails() {
		
		// Test emails...
		 EmailBuilder emb=new EmailBuilder();
		 Date date= new Date();
		 Timestamp currentTime=(new Timestamp(date.getTime()));
		 
		Email em1=null;
		em1= emb.setTo("bee, sk")
				 .setFrom("mus")
				 .setSubject("Hey guys whats up you both?")
				 .setBody("Buthainah, please work hard :p Sukhjinder, dont slack off")
				 .setRead(false)
				 .setTimestamp(currentTime)
				 .setPendingRecepients("bee, sk")
				 .build();
		Email em2= null; 
		em2= emb.setTo("sk")
				 .setFrom("mus")
				 .setSubject("Sukhjinder, dont go soon today?")
				 .setBody("Can you speak punjabi?")
				 .setRead(false)
				 .setTimestamp(currentTime)
				 .setPendingRecepients("sk")
				 .build();
	
		Email em3= null; 
		em3= emb.setTo("bee")
				 .setFrom("mus")
				 .setSubject("Buthainah!!!")
				 .setBody("Bee? Seriously?")
				 .setRead(false)
				 .setTimestamp(currentTime)
				 .setPendingRecepients("bee")
				 .build();
		
		Email em4 = null;
		em4= emb.setTo("mus, bee")
				.setFrom("sk")
				.setSubject("Hello from sk 1")
				.setBody("Hello Bee and Musab, Test email 1 from sk")
				.setRead(false)
				.setTimestamp(currentTime)
				.setPendingRecepients("bee, mus")
				.build();

				Email em5=null;
				em5= emb.setTo("mus")
				.setFrom("sk")
				.setSubject("Hello from sk 2")
				.setBody("Hello Musab, Test email 2 from sk")
				.setRead(false)
				.setTimestamp(currentTime)
				.setPendingRecepients("mus")
				.build();

				Email em6=null;
				em6= emb.setTo("bee")
				.setFrom("sk")
				.setSubject("Hello from sk 3")
				.setBody("Hello bee, Test email 3 from sk")
				.setRead(false)
				.setTimestamp(currentTime)
				.setPendingRecepients("bee")
				.build();

				Email em7=null;
				em7= emb.setTo("bee, sk")
				.setFrom("sk")
				.setSubject("Hello from sk 4")
				.setBody("Hello bee, send to myself as well...ha ha ha")
				.setRead(false)
				.setTimestamp(currentTime)
				.setPendingRecepients("sk, bee")
				.build();
		
		
				Email em8 = null;
				em8= emb.setTo("mus,sk")
					    .setFrom("bee")
					     .setSubject("Hey team mates!")
					    .setBody("Last project meeting eh?")
					     .setRead(false)
					     .setTimestamp(currentTime)
					    .setPendingRecepients("mus,sk")
					     .build();
					 
					 

					  Email em9=null;
					   em9= emb.setTo("mus")
					    .setFrom("bee")
					     .setSubject("Hey musab!")
					    .setBody("Last project meeting eh?")
					     .setRead(false)
					     .setTimestamp(currentTime)
					    .setPendingRecepients("mus")
					     .build();
					 
					 
					 
					  Email em10=null;

					   em10= emb.setTo("sk")
					 
					    .setFrom("bee")

					     .setSubject("Hey sk!")
					 
					    .setBody("Good work on the conflict manager!")

					     .setRead(false)

					     .setTimestamp(currentTime)
					 
					    .setPendingRecepients("sk")

					     .build();
					 
					 
					 
					 
					  Email em11=null;

					   em11= emb.setTo("anonymous, mus")
					 
					    .setFrom("bee")

					     .setSubject("Hey anonymous!")
					 
					    .setBody("Why haven't you logged on in a while?")

					     .setRead(false)

					     .setTimestamp(currentTime)
					 
					    .setPendingRecepients("anonymous, mus")

					     .build();
				
				
		
		try {
			em1.save();
			em2.save();
			em3.save();
			em4.save();
			em5.save();
			em6.save();
			em7.save();
			em8.save();
			em9.save();
			em10.save();
			em11.save();
			
			
		} catch (ModelException e1) {
			System.out.println("Server: Could not save emails.");
			System.exit(0);
		}
		/*  -------- Temporary testing code ends */
	}

	/**
	 * Load all the email objects from the database
	 * @throws DaoException 
	 */
	private void loadEmails() throws DaoException{
		Map<Integer, String[]> mail = null;

		try {
			mail = daoFactory.getEmailDao().findAll();
			for (Map.Entry<Integer, String[]> entryMail : mail.entrySet()) {
				String mailArray[] = entryMail.getValue();
				Email emailObj = new EmailBuilder()
									.setTo(mailArray[0])
									.setFrom(mailArray[1])
									.setSubject(mailArray[2])
									.setBody(mailArray[3])
									.setRead(Boolean.valueOf(mailArray[4]))
									.setTimestamp(new Timestamp(Long.valueOf(mailArray[5])))
									.setPendingRecepients(mailArray[6])
									.setId(entryMail.getKey())
									.build();
				emailsToSend.add(emailObj);
			}
		} catch (DaoException e) {
			throw new DaoException(e.getMessage());
		}
	}
	
	/**
	 * Initializes the dao factory
	 */
	private void initDao() {
		try {
			daoFactory = new DaoFactory();
		} catch (SQLException e) {
			System.out.println("Server: Could not initialize dao, exiting..");
			System.exit(0);
		} catch (DaoException e) {
			System.out.println("Server: Could not initialize dao, exiting..");
			System.exit(0);
		}
	}


	/**
	 * Getter for port
	 * @return
	 */
	public int getPort(){
		return port;
	}

	/**
	 * Adds a client object to the exiting client list, replacing the exiting one if with the same name
	 * 
	 * @param client
	 */
	public synchronized void addOrUpdate(Client client) {
		
		for (Iterator<Client> iterator = connectedClients.iterator(); iterator.hasNext(); ) {
		    Client c = iterator.next();
		    if (c.getName().trim().equalsIgnoreCase(client.getName())){
		    	c.setSocket(client.getSocket());
				return;
		    }
		}
		connectedClients.add(client);		
	}


	/**
	 * Returns list of emails to send;
	 * 
	 * @return
	 */
	public List<Email> getEmailsToSend(){
		return emailsToSend;
	}

	/**
	 * Given a username, gets all the emails which this user is supposed to get
	 * 
	 * @return
	 */
	public ArrayList<Email> getPendingEmailsFor(String username) {
		
		ArrayList<Email> toReturn = new ArrayList<Email>();
		for(Email email : emailsToSend){
			if (email.hasPendingRecepient(username)){
				toReturn.add(email);
			}
		}
		return toReturn;
	}
	
	/**
	 * Given an array list of emails, deletes them permanently from memory and persistent
	 * 
	 * @param emailsToDestroy
	 * @throws DaoException 
	 */
	public synchronized void destroyEmails(List<Email> emailsToDestroy) throws DaoException{
		
		Set<Integer> emailIdsToDelete = new HashSet<Integer>();
		// Get all ids to be deleted
		for(Email em : emailsToDestroy){
			emailIdsToDelete.add(em.getId());
		}
		
		// Delete them from the dao
		daoFactory.getEmailDao().deleteById(emailIdsToDelete);
		emailsToSend.removeAll(emailsToDestroy);
	}

	/**
	 * Updates the emails array given in the dao
	 * @param emailsToUpdate
	 * @throws ModelException 
	 */
	public synchronized void updateEmails(List<Email> emailsToUpdate) throws ModelException {
		for(Email email : emailsToUpdate){
			email.save();
		}
	}
	
	/**
	 * Getter for online clients
	 * @return
	 */
	public List<Client> getConnectedClients(){
		return connectedClients;
	}
	
	/**
	 * Tells if a given client is online or not
	 * 
	 * @param username
	 * @return
	 */
	public Client getClient(String username){
		for (Client client : connectedClients){
			if (client.getName().trim().equalsIgnoreCase(username)){
				return client;
			}
		}
		return null;
	}

	/**
	 * Inserts these emails into the database and also adds them to the emails to send list
	 * @param emailsToPersist
	 */
	public synchronized void persistEmailsToBeSent(List<Email> emailsToPersist) {
		
		for(Email email : emailsToPersist){
			try {
				email.save();
				emailsToSend.add(email);
			} catch (ModelException e) {
				System.out.println("Could not save email due to some reason");
			}
		}
		
	}

	/**
	 * Sets the emails to send
	 * @param emailsToSend
	 */
	public void setEmailsToSend(List<Email> emailsToSend) {
		this.emailsToSend = emailsToSend;
	}
	
}
